]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/Tests/Unit Tests/LocalOnlyWithInterfacesTest.m
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Tests / Unit Tests / LocalOnlyWithInterfacesTest.m
1 /*
2 * Copyright (c) 2019 Apple Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "unittest_common.h"
18 #import <XCTest/XCTest.h>
19
20 struct UDPSocket_struct
21 {
22 mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
23 };
24 typedef struct UDPSocket_struct UDPSocket;
25
26 // This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
27 char test_query_any_msgbuf[35] = {
28 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
29 0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
30 0x01, 0x00, 0x01
31 };
32
33 // Modified for different scopes
34 char test_query_local_msgbuf[35] = {
35 0x00, 0x00, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
36 0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
37 0x01, 0x00, 0x01
38 };
39
40 char test_query_interface_msgbuf[35] = {
41 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
42 0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
43 0x01, 0x00, 0x01
44 };
45
46 // Variables associated with contents of the above uDNS message
47 mDNSlocal char test_domainname_cstr[] = "123server.dotbennu.com.";
48
49 mDNSlocal mDNSBool _TestCreateEtcHostsEntryWithInterfaceID(const domainname *domain, const struct sockaddr *sa, const domainname *cname, mDNSInterfaceID interfaceID, AuthHash *auth)
50 { // Copied from mDNSMacOSXCreateEtcHostsEntry
51 AuthRecord *rr;
52 mDNSu32 namehash;
53 AuthGroup *ag;
54 mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly;
55 mDNSu16 rrtype;
56
57 if (!domain)
58 {
59 LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! name NULL");
60 return mDNSfalse;
61 }
62 if (!sa && !cname)
63 {
64 LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! sa and cname both NULL");
65 return mDNSfalse;
66 }
67
68 if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
69 {
70 LogMsg("_TestCreateEtcHostsEntryWithInterfaceID: ERROR!! sa with bad family %d", sa->sa_family);
71 return mDNSfalse;
72 }
73
74
75 if (interfaceID)
76 {
77 InterfaceID = interfaceID;
78 }
79
80 if (sa)
81 rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA);
82 else
83 rrtype = kDNSType_CNAME;
84
85 // Check for duplicates. See whether we parsed an entry before like this ?
86 namehash = DomainNameHashValue(domain);
87 ag = AuthGroupForName(auth, namehash, domain);
88 if (ag)
89 {
90 rr = ag->members;
91 while (rr)
92 {
93 if (rr->resrec.rrtype == rrtype)
94 {
95 if (rrtype == kDNSType_A)
96 {
97 mDNSv4Addr ip;
98 ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
99 if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip) && InterfaceID == rr->resrec.InterfaceID)
100 {
101 LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same IPv4 address and InterfaceID for name %##s ID %d", domain->c, IIDPrintable(InterfaceID));
102 return mDNSfalse;
103 }
104 }
105 else if (rrtype == kDNSType_AAAA)
106 {
107 mDNSv6Addr ip6;
108 ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
109 ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
110 ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
111 ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
112 if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6) && InterfaceID == rr->resrec.InterfaceID)
113 {
114 LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same IPv6 address and InterfaceID for name %##s ID %d", domain->c, IIDPrintable(InterfaceID));
115 return mDNSfalse;
116 }
117 }
118 else if (rrtype == kDNSType_CNAME)
119 {
120 if (SameDomainName(&rr->resrec.rdata->u.name, cname))
121 {
122 LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Same cname %##s for name %##s", cname->c, domain->c);
123 return mDNSfalse;
124 }
125 }
126 }
127 rr = rr->next;
128 }
129 }
130 rr = (AuthRecord *) callocL("etchosts", sizeof(*rr));
131 if (rr == NULL) return mDNSfalse;
132 mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL);
133 AssignDomainName(&rr->namestorage, domain);
134
135 if (sa)
136 {
137 rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr);
138 if (sa->sa_family == AF_INET)
139 rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr;
140 else
141 {
142 rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0];
143 rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1];
144 rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2];
145 rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3];
146 }
147 }
148 else
149 {
150 rr->resrec.rdlength = DomainNameLength(cname);
151 rr->resrec.rdata->u.name.c[0] = 0;
152 AssignDomainName(&rr->resrec.rdata->u.name, cname);
153 }
154 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
155 SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us
156 LogInfo("_TestCreateEtcHostsEntryWithInterfaceID: Adding resource record %s ID %d", ARDisplayString(&mDNSStorage, rr), IIDPrintable(rr->resrec.InterfaceID));
157 InsertAuthRecord(&mDNSStorage, auth, rr);
158 return mDNStrue;
159 }
160
161 mDNSlocal mStatus InitEtcHostsRecords(void)
162 {
163 mDNS *m = &mDNSStorage;
164 struct sockaddr_storage hostaddr;
165 domainname domain;
166
167 AuthHash newhosts;
168 mDNSPlatformMemZero(&newhosts, sizeof(AuthHash));
169
170 memset(&hostaddr, 0, sizeof(hostaddr));
171 get_ip("10.0.0.201", &hostaddr);
172 MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
173 _TestCreateEtcHostsEntryWithInterfaceID(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSInterface_P2P, &newhosts);
174
175 memset(&hostaddr, 0, sizeof(hostaddr));
176 get_ip("10.0.0.202", &hostaddr);
177 MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
178 mDNSMacOSXCreateEtcHostsEntry_ut(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, mDNSNULL, &newhosts);
179
180 memset(&hostaddr, 0, sizeof(hostaddr));
181 get_ip("10.0.0.203", &hostaddr);
182 MakeDomainNameFromDNSNameString(&domain, "123server.dotbennu.com");
183 _TestCreateEtcHostsEntryWithInterfaceID(&domain, (struct sockaddr *) &hostaddr, mDNSNULL, primary_interfaceID, &newhosts);
184
185 UpdateEtcHosts_ut(&newhosts);
186 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
187 mDNS_Execute(m);
188
189 return mStatus_NoError;
190 }
191
192 mDNSlocal mDNSs32 NumReplies(reply_state * reply)
193 {
194 mDNSs32 result = 0;
195 reply_state * nextreply = reply;
196 while(nextreply) { result++; nextreply = nextreply->next;}
197 return result;
198 }
199
200 mDNSlocal mDNSBool HasReplyWithInterfaceIndex(reply_state * reply, mDNSu32 interfaceIndex)
201 {
202 mDNSBool result = mDNSfalse;
203 reply_state * nextreply = reply;
204 while(nextreply)
205 {
206 result = (ntohl(nextreply->rhdr[0].ifi) == interfaceIndex);
207 if (result) break;
208 nextreply = nextreply->next;
209 }
210 return result;
211 }
212
213 @interface LocalOnlyWithInterfacesTest : XCTestCase
214 {
215 UDPSocket* local_socket;
216 request_state* client_request_message;}
217 @end
218
219 @implementation LocalOnlyWithInterfacesTest
220
221 // The InitThisUnitTest() initializes the mDNSResponder environment as well as
222 // a DNSServer. It also allocates memory for a local_socket and client request.
223 // Note: This unit test does not send packets on the wire and it does not open sockets.
224 - (void)setUp
225 {
226 mDNSPlatformMemZero(&mDNSStorage, sizeof(mDNS));
227
228 // Init unit test environment and verify no error occurred.
229 mStatus result = init_mdns_environment(mDNStrue);
230 XCTAssertEqual(result, mStatus_NoError);
231
232 // Add one DNS server and verify it was added.
233 AddDNSServer_ut();
234 XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
235
236 AddDNSServerScoped_ut(primary_interfaceID, kScopeInterfaceID);
237 XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 2);
238
239 // Populate /etc/hosts
240 result = InitEtcHostsRecords();
241 XCTAssertEqual(result, mStatus_NoError);
242
243 int count = LogEtcHosts_ut(&mDNSStorage);
244 XCTAssertEqual(count, 3);
245
246 // Create memory for a socket that is never used or opened.
247 local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
248
249 // Create memory for a request that is used to make this unit test's client request.
250 client_request_message = calloc(1, sizeof(request_state));
251 }
252
253 - (void)tearDown
254 {
255 mDNS *m = &mDNSStorage;
256 request_state* req = client_request_message;
257 DNSServer *ptr, **p = &m->DNSServers;
258
259 while (req->replies)
260 {
261 reply_state *reply = req->replies;
262 req->replies = req->replies->next;
263 mDNSPlatformMemFree(reply);
264 }
265 mDNSPlatformMemFree(req);
266
267 mDNSPlatformMemFree(local_socket);
268
269 while (*p)
270 {
271 ptr = *p;
272 *p = (*p)->next;
273 LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
274 mDNSPlatformMemFree(ptr);
275 }
276 }
277
278 // This unit test tries 3 different local cache queries
279 // 1) Test the Any query does not receive the entry scoped to the primary interface, but does received the local and P2P entries
280 // 2) Test the LocalOnly query receives all the entries
281 // 3) Test the interface scoped query receives the interface scoped entry
282 - (void)testLocalOnlyWithInterfacesTestSeries
283 {
284 request_state* req = client_request_message;
285
286 fprintf(stdout, "testLocalOnlyWithInterfacesTestSeries: primary_interfaceID %d\n", primary_interfaceID);
287
288 // Verify Any index returns 2 results.
289 #if !TARGET_OS_WATCH
290 if (primary_interfaceID)
291 {
292 // Path evaluation on watch causes this query to get scoped to en0 (primary_interfaceID) so it's the same as #3
293 [self _executeClientQueryRequest: req andMsgBuf: test_query_any_msgbuf];
294 XCTAssertEqual(NumReplies(req->replies), 2);
295 XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexP2P));
296 XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexLocalOnly));
297 }
298 else
299 {
300 fprintf(stdout, "testLocalOnlyWithInterfacesTestSeries: skipping test_query_any_msgbuf test because interface not found\n");
301 }
302 #endif
303
304 // Verify LocalOnly index returns 3 results.
305 [self _executeClientQueryRequest: req andMsgBuf: test_query_local_msgbuf];
306 XCTAssertEqual(NumReplies(req->replies), 3);
307 XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexP2P));
308 XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, kDNSServiceInterfaceIndexLocalOnly));
309 if (primary_interfaceID) XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, primary_interfaceID));
310
311 if (primary_interfaceID)
312 {
313 // Verify en0 index returns 1 result.
314 test_query_interface_msgbuf[7] = primary_interfaceID;
315 [self _executeClientQueryRequest: req andMsgBuf: test_query_interface_msgbuf];
316 XCTAssertEqual(NumReplies(req->replies), 1);
317 XCTAssertTrue(HasReplyWithInterfaceIndex(req->replies, primary_interfaceID));
318 }
319 else
320 {
321 fprintf(stdout, "testLocalOnlyWithInterfacesTestSeries: skipping primary_interfaceID test because interface not found\n");
322 }
323 }
324
325 // Simulate a uds client request by setting up a client request and then
326 // calling mDNSResponder's handle_client_request. The handle_client_request function
327 // processes the request and starts a query. This unit test verifies
328 // the client request and query were setup as expected. This unit test also calls
329 // mDNS_execute which determines the cache does not contain the new question's
330 // answer.
331 - (void)_executeClientQueryRequest: (request_state*)req andMsgBuf: (char*)msgbuf
332 {
333 mDNS *const m = &mDNSStorage;
334 char *msgptr = msgbuf;
335 size_t msgsz = sizeof(test_query_local_msgbuf);
336 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
337 DNSQuestion *q;
338 mStatus err = mStatus_NoError;
339 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
340
341 // Process the unit test's client request
342 start_client_request(req, msgptr, msgsz, query_request, local_socket);
343 XCTAssertEqual(err, mStatus_NoError);
344
345 // Verify the request fields were set as expected
346 XCTAssertNil((__bridge id)req->next);
347 XCTAssertNil((__bridge id)req->primary);
348 XCTAssertEqual(req->sd, client_req_sd);
349 XCTAssertEqual(req->process_id, client_req_process_id);
350 XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
351 XCTAssertEqual(req->validUUID, mDNSfalse);
352 XCTAssertEqual(req->errsd, 0);
353 XCTAssertEqual(req->uid, client_req_uid);
354 XCTAssertEqual(req->ts, t_complete);
355 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
356 XCTAssertEqual(req->msgend, msgptr+msgsz);
357 XCTAssertNil((__bridge id)(void*)req->msgbuf);
358 XCTAssertEqual(req->hdr.version, VERSION);
359 XCTAssertNil((__bridge id)req->replies);
360 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
361 XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
362
363 // Verify the query fields were set as expected
364 q = &req->u.queryrecord.op.q;
365 XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
366 if (m->Questions)
367 {
368 XCTAssertEqual(q, m->Questions);
369 XCTAssertEqual(q, m->NewQuestions);
370 XCTAssertTrue(q->InterfaceID == mDNSInterface_Any || q->InterfaceID == primary_interfaceID);
371 }
372 else
373 {
374 XCTAssertEqual(q, m->LocalOnlyQuestions);
375 XCTAssertEqual(q, m->NewLocalOnlyQuestions);
376 XCTAssertEqual(q->InterfaceID, mDNSInterface_LocalOnly);
377 }
378 XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
379 XCTAssertEqual(q->ReturnIntermed, mDNStrue);
380 XCTAssertEqual(q->Suppressed, mDNSfalse);
381
382 ConvertDomainNameToCString(&q->qname, qname_cstr);
383 XCTAssertFalse(strcmp(qname_cstr, test_domainname_cstr));
384 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
385
386 XCTAssertEqual(q->flags, req->flags);
387 XCTAssertEqual(q->qtype, 1);
388 XCTAssertEqual(q->qclass, 1);
389 XCTAssertEqual(q->LongLived, 0);
390 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
391 XCTAssertEqual(q->ForceMCast, 0);
392 XCTAssertEqual(q->TimeoutQuestion, 0);
393 XCTAssertEqual(q->WakeOnResolve, 0);
394 XCTAssertEqual(q->UseBackgroundTraffic, 0);
395 XCTAssertEqual(q->ProxyQuestion, 0);
396 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
397 XCTAssertEqual(q->AppendSearchDomains, 0);
398 XCTAssertNil((__bridge id)q->DuplicateOf);
399
400 // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
401 // It won't be yet because the cache is empty.
402 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
403 mDNS_Execute(m);
404
405 // Verify mDNS_Execute processed the new question.
406 XCTAssertNil((__bridge id)m->NewQuestions);
407 XCTAssertNil((__bridge id)m->NewLocalOnlyQuestions);
408 XCTAssertEqual(m->rrcache_totalused, 0);
409 m->Questions = nil; // Reset
410 }
411
412
413 @end